package com.example.framework.common.exception;

import cn.hutool.core.util.StrUtil;
import com.example.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.google.common.annotations.VisibleForTesting;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.event.Level;

import java.io.Serial;

/**
 * 业务逻辑异常 Exception
 * 流程：
 * <ol>
 *     <li>在 {@link GlobalErrorCodeConstants} 定义错误码
 *     <li>throw {@link ServiceException#of(ErrorCode)}
 *     <li>GlobalExceptionHandler 捕获 {@link ServiceException}
 *     <li>取出code和msg，封装成R返回
 * </ol>
 * 所以不要随便new ErrorCode，虽然改成枚举类虽然可以很大程度避免，但是在微服务拆分的时候，
 * 不同服务的ErrorCode都需要写在枚举类里，会修改很频繁，因此要在各个服务按规定格式定义ErrorCode
 */
@Slf4j
@Data
@EqualsAndHashCode(callSuper = true)
public class ServiceException extends RuntimeException {

    /**
     * serialVersionUID
     */
    @Serial
    private static final long serialVersionUID = -5885155226898287919L;

    /**
     * 业务错误码
     *
     * @see GlobalErrorCodeConstants
     */
    private Integer code;
    /**
     * 错误提示
     */
    private String message;

    /**
     * 日志级别
     */
    private Level logLevel = Level.ERROR;

    private ServiceException() {
    }

    private ServiceException(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    private ServiceException(Integer code, String message, Level logLevel) {
        this.code = code;
        this.message = message;
        this.logLevel = logLevel;
    }

    public static ServiceException warn(ErrorCode errorCode) {
        return of(errorCode, Level.WARN);
    }

    public static ServiceException warn(ErrorCode errorCode, Object... params) {
        return of(errorCode, Level.WARN, params);
    }

    public static ServiceException of(ErrorCode errorCode, Level logLevel) {
        return of(errorCode.getCode(), errorCode.getMsg(), logLevel);
    }

    public static ServiceException of(ErrorCode errorCode, Level logLevel, Object... params) {
        return of(errorCode.getCode(), errorCode.getMsg(), logLevel, params);
    }

    public static ServiceException of(ErrorCode errorCode) {
        return of(errorCode.getCode(), errorCode.getMsg());
    }

    public static ServiceException of(ErrorCode errorCode, Object... params) {
        return of(errorCode.getCode(), errorCode.getMsg(), params);
    }

    /**
     * 尽量用ErrorCode作为入参，统一返回code
     * 如果实在要用，就用一个统一的code（-1）作为自定义异常的消息code
     *
     * @param message 自定义抛出的错误信息
     */
    public static ServiceException of(String message) {
        return of(GlobalErrorCodeConstants.CUSTOM_ERROR.getCode(), message);
    }

    public static ServiceException of(Integer code, String messagePattern, Level logLevel, Object... params) {
        String message = doFormat(messagePattern, params);
        return new ServiceException(code, message, logLevel);
    }

    public static ServiceException of(Integer code, String messagePattern, Object... params) {
        String message = doFormat(messagePattern, params);
        return new ServiceException(code, message);
    }

    @VisibleForTesting
    public static String doFormat(String messagePattern, Object... params) {
        return StrUtil.format(messagePattern, params);
    }

}
